#include "event/znx_tcp_proc.h"
#include "event/znx_worker_proc.h"
#include "http/znx_http_request.h"
#include <signal.h>


void read_response_0(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_0, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_0(znx_conn_t *conn)
{
    znx_result_t    res;
    char *str = "GET /0.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Header1: value1\r\n"
        "Header2: value2\r\n"
        "Header3: value3\r\n"
        "Transfer-Encoding: chunked\r\n"
        "\r\n"
        "3\r\naaa\r\n"
        "3\r\nbbb\r\n"
        "3\r\nccc\r\n"
        "0\r\n\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_0;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_1(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_1, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_1(znx_conn_t *conn)
{
    znx_result_t        res;
    char *str = "GET /1.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Header1: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
        "Header2: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
        "Header3: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
        "Header4: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
#if 1
        "header5: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n"
#endif
        "Transfer-Encoding: chunked\r\n"
        "\r\n"
        "1F4\r\n"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "\r\n"
        "1F4\r\n"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "\r\n"
        "0\r\n\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_1;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_2(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_2, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_2(znx_conn_t *conn)
{
    znx_result_t        res;
    char *str = "GET /2.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Content-Length: 5\r\n"
        "\r\n"
        "abcde"
        "\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_2;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_3(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_3, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_3(znx_conn_t *conn)
{
    znx_result_t        res;
    char *str = "GET /3.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Content-Length: 1000\r\n"
        "\r\n"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_3;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_4(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_4, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_4(znx_conn_t *conn)
{
    // parse_after_body_line, testing body_buf cannot store the last scene '\n'
    znx_result_t        res;
    char *str = "GET /4.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Content-Length: 832\r\n"
        "\r\n"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        "\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_4;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_5(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res <= 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res > 0, but got %d", res);
    }

    znx_str_t tmp;
    tmp.data = buf; tmp.len = (size_t)res;
    ZNX_DEF_LOG_DEBUG(NULL,
        "############# response_5, len: %d ###########\n%v#########", res, &tmp);

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_5(znx_conn_t *conn)
{
    znx_result_t    res;
    char *str = "GET /5.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Header1: value1\r\n"
        "Header2: value2\r\n"
        "Header3: value3\r\n"
        "Transfer-Encoding: chunked\r\n"
        "Trailer: Aaa,Bbb,Ccc\r\n"
        "\r\n"
        "3\r\naaa\r\n"
        "3\r\nbbb\r\n"
        "3\r\nccc\r\n"
        "0\r\n"
        "Aaa: 123\r\n"
        "Bbb: 456\r\n"
        "Ccc: 789\r\n"
        "\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_5;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void read_response_6(znx_event_t *event)
{
    znx_conn_t *conn = event->data;

    u_char buf[512];
    znx_result_t res;
    res = znx_conn_read(conn, buf, 512);
    if (res == ZNX_AGAIN) {
        znx_event_loop_enable_read(conn);
        return;
    }

    if (res != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect res == 0, but got %d", res);
    }

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void do_request_6(znx_conn_t *conn)
{
    znx_result_t    res;
    // invalid request body
    char *str = "GET /6.txt HTTP/1.1\r\n"
        "Host: www.aaa.com\r\n"
        "Header1: value1\r\n"
        "Header2: value2\r\n"
        "Header3: value3\r\n"
        "Transfer-Encoding: chunked\r\n"
        "Trailer: Aaa,Bbb,Ccc\r\n"
#if 0
        "\r\n"
#endif
        "3\r\naaa\r\n"
        "3\r\nbbb\r\n"
        "3\r\nccc\r\n"
        "0\r\n"
        "Aaa: 123\r\n"
        "Bbb: 456\r\n"
        "Ccc: 789\r\n"
        "\r\n";

    res = znx_conn_write(conn, str, strlen(str));
    if (res != (znx_result_t)strlen(str)) {
        ZNX_DEF_LOG_FATAL(NULL, "expect %d, but got other", res);
    }

    conn->read.handler = read_response_6;
    znx_event_loop_start_io(conn);
    znx_event_loop_enable_read(conn);
}


void znx_test_client_conn_handler(void *data, znx_conn_t *conn, znx_result_t res)
{
    if (res != ZNX_SUCCESS) {
        ZNX_DEF_LOG_FATAL(NULL, "expect connect success, but got other");
    }

    int type_data = (int)(intptr_t)data;
    if (type_data == 0) {
        do_request_0(conn);
        return;
    }

    if (type_data == 1) {
        do_request_1(conn);
        return;
    }

    if (type_data == 2) {
        do_request_2(conn);
        return;
    }

    if (type_data == 3) {
        do_request_3(conn);
        return;
    }

    if (type_data == 4) {
        do_request_4(conn);
        return;
    }

    if (type_data == 5) {
        do_request_5(conn);
        return;
    }

    if (type_data == 6) {
        do_request_6(conn);
        return;
    }

    znx_event_loop_remove_conn(conn);
    znx_conn_free(conn);
}


void znx_test_client_request(znx_dispatch_event_t *dispatch_event)
{
    znx_address_t   address;
    bzero(&address, sizeof(znx_address_t));
    if (!znx_address_parser((u_char *)"127.0.0.1:8889",
        strlen("127.0.0.1:8889"), &address, NULL))
    {
        ZNX_DEF_LOG_FATAL(NULL, "parse 127.0.0.1:8889 failed");
    }

    znx_tcp_connect_opt_t opt = {
        .nodelay = 1,
        .quickack = 1
    };

    znx_conn_t  *conn;
    conn = znx_thread_calloc(sizeof(znx_conn_t));
    znx_conn_init(conn, ZNX_CONN_EPOLL_ET);
    znx_tcp_connect_setup(conn, &address, NULL, &opt, 20,
        znx_test_client_conn_handler, dispatch_event->data);

    free(dispatch_event);
}


void znx_test_http_do_request()
{
    for (int i = 0; i < 7; i++) {
        znx_dispatch_event_t    *dispatch_event;
        dispatch_event = malloc(sizeof(znx_dispatch_event_t));
        bzero(dispatch_event, sizeof(znx_dispatch_event_t));
        dispatch_event->data = (void *)(intptr_t)i;
        dispatch_event->handler = znx_test_client_request;
        znx_worker_processor_post_event(dispatch_event);
    }
}


void response_handler(znx_http_request_t *request, znx_result_t res)
{
    znx_http_response_t     *response;
    znx_pool_t              *pool;
    znx_http_conn_t         *hc;

    response = request->response;
    hc = request->hc;
    pool = request->pool;

    if (res != ZNX_SUCCESS) {
        ZNX_DEF_LOG_FATAL(NULL, "expect success, but got %d", res);
    }

    znx_http_request_destroy(request);
    znx_http_conn_close(hc);
    znx_pool_destroy(pool);
}


void do_response(znx_http_request_t *request)
{
    znx_http_response_t     *response;
    znx_http_header_t       *header;
    znx_buf_t               *body_buf;
    znx_http_conn_t         *hc;
    znx_pool_t              *pool;

    response = request->response;
    hc = request->hc;
    pool = request->pool;

    response->status_code = ZNX_HTTP_OK;

    if (request->uri.len == 6 &&
        znx_strncmp(request->uri.data, "/0.txt", 6) == 0)
    {
        header = znx_pool_calloc(request->pool, sizeof(znx_http_request_t));
        header->key.data = (u_char *)ZNX_HTTP_TRANSFER_ENCODING_HDR;
        header->key.len = ZNX_HTTP_TRANSFER_ENCODING_HDR_LEN;
        header->val.data = (u_char *)ZNX_HTTP_CHUNKED;
        header->val.len = ZNX_HTTP_CHUNKED_LEN;
        znx_http_headers_add(&response->headers, header);

        body_buf = znx_buf_alloc_ext(512);
        body_buf->end = znx_snprintf(body_buf->end, 512, "response /0.txt use transfer-encoding");
        znx_buf_chain_enqueue(response->body_chain, body_buf, ZNX_FALSE);
        znx_http_send_response_header(request);
        znx_http_set_send_response_body_done(request);
        znx_http_start_send_response_body(request, response_handler, NULL);
        return;
    }

    if (request->uri.len == 6 &&
        znx_strncmp(request->uri.data, "/1.txt", 6) == 0)
    {
        body_buf = znx_buf_alloc_ext(512);
        body_buf->end = znx_snprintf(body_buf->end, 512, "response /1.txt use transfer-encoding");
        znx_buf_chain_enqueue(response->body_chain, body_buf, ZNX_FALSE);
        znx_http_send_response_header(request);
        znx_http_set_send_response_body_done(request);
        znx_http_start_send_response_body(request, response_handler, NULL);
        return;
    }

    if (request->uri.len == 6 &&
        znx_strncmp(request->uri.data, "/2.txt", 6) == 0)
    {
        header = znx_pool_calloc(request->pool, sizeof(znx_http_request_t));
        header->key.data = (u_char *)ZNX_HTTP_CONTENT_LENGTH_HDR;
        header->key.len = ZNX_HTTP_CONTENT_LENGTH_HDR_LEN;
        header->val.data = (u_char *)"34";
        header->val.len = 2;
        znx_http_headers_add(&response->headers, header);

        body_buf = znx_buf_alloc_ext(512);
        body_buf->end = znx_snprintf(body_buf->end, 512, "response /2.txt use content-length");
        znx_buf_chain_enqueue(response->body_chain, body_buf, ZNX_FALSE);
        znx_http_send_response_header(request);
        znx_http_set_send_response_body_done(request);
        znx_http_start_send_response_body(request, response_handler, NULL);
        return;
    }

    if (request->uri.len == 6 &&
        znx_strncmp(request->uri.data, "/3.txt", 6) == 0)
    {
        response->trailers = znx_http_trailers_create(request->pool);
        header = znx_pool_calloc(request->pool, sizeof(znx_http_header_t));
        header->key.data = (u_char *)"AAA";
        header->key.len = 3;
        header->val.data = (u_char *)"aaa";
        header->val.len = 3;
        znx_http_headers_add(&response->trailers->headers, header);
        header = znx_pool_calloc(request->pool, sizeof(znx_http_header_t));
        header->key.data = (u_char *)"BBB";
        header->key.len = 3;
        header->val.data = (u_char *)"bbb";
        header->val.len = 3;
        znx_http_headers_add(&response->trailers->headers, header);
        header = znx_pool_calloc(request->pool, sizeof(znx_http_header_t));
        header->key.data = (u_char *)"CCC";
        header->key.len = 3;
        header->val.data = (u_char *)"ccc";
        header->val.len = 3;
        znx_http_headers_add(&response->trailers->headers, header);

        body_buf = znx_buf_alloc_ext(512);
        body_buf->end = znx_snprintf(body_buf->end, 512, "response /3.txt use transfer-encoding");
        znx_buf_chain_enqueue(response->body_chain, body_buf, ZNX_FALSE);
        znx_http_send_response_header(request);
        znx_http_set_send_response_body_done(request);
        znx_http_start_send_response_body(request, response_handler, NULL);
        return;
    }

    body_buf = znx_buf_alloc_ext(512);
    body_buf->end = znx_snprintf(body_buf->end, 512, "response other request use transfer-encoding");
    znx_buf_chain_enqueue(response->body_chain, body_buf, ZNX_FALSE);
    znx_http_send_response_header(request);
    znx_http_set_send_response_body_done(request);
    znx_http_start_send_response_body(request, response_handler, NULL);
}


static void
read_request_body_callbak(znx_http_request_t *request, znx_result_t res)
{
    znx_http_conn_t     *hc;
    znx_pool_t          *pool;
    znx_buf_t           *body_buf;
    znx_queue_t         *q;
    size_t              consume = 0;
    znx_str_t           tmp;
    znx_http_header_t   *header;

    hc = request->hc;
    pool = request->pool;

    if (res != ZNX_AGAIN && res != ZNX_SUCCESS) {
        if (request->uri.len != sizeof("/6.txt") - 1 ||
            znx_strncmp(request->uri.data, "/6.txt", sizeof("/6.txt") - 1) != 0)
        {
            ZNX_DEF_LOG_FATAL(NULL, "expect /6.txt, but got %v", &request->uri);
        }

        ZNX_DEF_LOG_DEBUG(NULL, "it's testing read request body failed, err: %d", res);
        znx_http_request_destroy(request);
        znx_http_conn_close(hc);
        znx_pool_destroy(pool);
        return;
    }

    q = znx_queue_last(&request->body_chain->head);
    while (q != &request->body_chain->head) {
        body_buf = znx_queue_data(q, znx_buf_t, queue);
        tmp.data = body_buf->pos;
        tmp.len = (size_t)(body_buf->end - body_buf->pos);
        consume += tmp.len;
        ZNX_DEF_LOG_DEBUG(NULL, "request body: %v", &tmp);
        q = q->prev;
    }

    ZNX_DEF_LOG_DEBUG(NULL, "consume: %d", consume);

    if (request->uri.len == sizeof("/0.txt") - 1 &&
        znx_strncmp(request->uri.data, "/0.txt", sizeof("/0.txt") - 1) == 0)
    {
        if (consume != 9) {
            ZNX_DEF_LOG_FATAL(NULL, "expect 9, but got %d", consume);
        }
    } else if (request->uri.len == sizeof("/1.txt") - 1 &&
        znx_strncmp(request->uri.data, "/1.txt", sizeof("/1.txt") - 1) == 0)
    {
        if (consume != 1000) {
            ZNX_DEF_LOG_FATAL(NULL, "expect 1000, but got %d", consume);
        }
    } else if (request->uri.len == sizeof("/2.txt") - 1 &&
        znx_strncmp(request->uri.data, "/2.txt", sizeof("/2.txt") - 1) == 0)
    {
        if (consume != 5) {
            ZNX_DEF_LOG_FATAL(NULL, "expect 5, but got %d", consume);
        }
    } else if (request->uri.len == sizeof("/3.txt") - 1 &&
        znx_strncmp(request->uri.data, "/3.txt", sizeof("/3.txt") - 1) == 0)
    {
        if (consume != 1000) {
            ZNX_DEF_LOG_FATAL(NULL, "expect 1000, but got %d", consume);
        }
    }

    znx_buf_chain_consume(request->body_chain, consume);

    if (znx_buf_chain_total_bytes(request->body_chain) != 0) {
        ZNX_DEF_LOG_FATAL(NULL, "expect 0, but got other");
    }

    if (request->trailers) {
        q = znx_queue_head(&request->trailers->headers.head);
        while (q != &request->trailers->headers.head) {
            header = znx_queue_data(q, znx_http_header_t, queue);
            q = q->next;
            ZNX_HTTP_LOG_DEBUG(request->hc->hlog, "#Trailer#%v: %v#", &header->key, &header->val);
        }
    }

    do_response(request);
}


static void
read_request_header_done(znx_http_request_t *request, znx_result_t res)
{
    if (res != ZNX_SUCCESS) {
        ZNX_DEF_LOG_DEBUG(NULL, "read header failed, err: %d", res);
        znx_http_conn_close(request->hc);
        znx_pool_destroy(request->pool);
        return;
    }

    znx_http_request_dump(request);

    znx_http_start_read_request_body(request, read_request_body_callbak, NULL);
}


void
znx_test_server_accept_handler(znx_event_t *event)
{
    znx_conn_t              *conn;
    znx_pool_t              *pool;
    znx_http_conn_t         *hc;
    znx_http_request_t      *request;
    znx_http_core_conf_t    *cf;
    
    conn = event->data;
    pool = znx_pool_create();
    hc = znx_pool_calloc(pool, sizeof(znx_http_conn_t));
    hc->hlog.log = &def_log;
    ZNX_STR_SET(&hc->hlog.context, "znx_test");
    znx_http_conn_init(hc, conn, ZNX_HTTP_NET_CONN);
    request = znx_http_request_create(pool, hc, ZNX_FALSE);

    cf = znx_http_core_conf_create(pool);
    znx_http_core_conf_check_and_fix(cf);
    cf->request_header_buf_size = 128;
    cf->max_request_header_size = 1024;
    cf->request_body_buf_size = 512,
    cf->read_request_header_timeout_ms = 10;
    cf->read_request_body_timeout_ms = 10;

    request->cf = cf;

    znx_http_start_read_request_headers(request, read_request_header_done, NULL);
}


int main()
{
    signal(SIGPIPE, SIG_IGN);
    znx_address_t address;
    bzero(&address, sizeof(znx_address_t));
    if (!znx_address_parser(
        (u_char *)"127.0.0.1:8889", strlen("127.0.0.1:8889"), &address, NULL))
    {
        ZNX_DEF_LOG_FATAL(NULL, "parser %s failed", "127.0.0.1:8889");
    }

    znx_proc_init();

    znx_def_log_init(0, LOG_LEVEL_INFO, NULL, LOG_ROLLING_DAY);

    znx_listener_t *l = znx_listener_alloc();
    l->listen_addr = "127.0.0.1:8889";
    l->address = address;
    l->backlog = 1024;
    l->nodelay = 1;
    l->reuseaddr = 1;
    l->accept_handler = znx_test_server_accept_handler;

    if (!znx_listener_open(l, INVALID_FD)) {
        ZNX_DEF_LOG_FATAL(NULL, "open listener 127.0.0.1:8889 failed");
    }

    znx_queue_insert_tail(&g_listeners->head, &l->queue);
    g_listeners->version++;

    znx_worker_processor_start(1, 1, 0);
    znx_tcp_accepter_start(2);

    znx_test_http_do_request();

    usleep(100000);

    znx_proc_shut_down();

    return 0;
}
